Attribute VB_Name = "modStackAllocZero"
'***************************************************************
' (c) Copyright 2000 Matthew J. Curland
'
' This file is from the CD-ROM accompanying the book:
' Advanced Visual Basic 6: Power Techniques for Everyday Programs
'   Author: Matthew Curland
'   Published by: Addison-Wesley, July 2000
'   ISBN: 0-201-70712-8
'   http://www.PowerVB.com
'
' You are entitled to license free distribution of any application
'   that uses this file if you own a copy of the book, or if you
'   have obtained the file from a source approved by the author. You
'   may redistribute this file only with express written permission
'   of the author.
'
' This file depends on:
'   References:
'     VBoostTypes6.olb (VBoost Object Types (6.0))
'   Files:
'     FunctionDelegator.bas
'   Minimal VBoost conditionals:
'     None
'   Conditional Compilation Values:
'     FUNCTIONDELEGATOR_NOHEAP = 1 'Minimize FunctionDelegator impact
'
' This file is discussed in Chapter 11.
'***************************************************************
Option Explicit
'StackAllocZero.Call(Bytes) returns a pointer to
'a chunk of bytes that are allocated on the stack
'of the current function.  This is the same code
'as _alloca in C, with modifications made to
'make it a _stdcall function, and to zero the
'memory before the pointer is returned.  The code
'that generated these bytes is shown at the end of
'the file.

Private Type StackAllocZeroCode
    Code(15) As Long
End Type
Private Code As StackAllocZeroCode
Private FD As FunctionDelegator
Private pCallObj As ICallLongReturnLong
Public Property Get StackAllocZero() As ICallLongReturnLong
    If pCallObj Is Nothing Then Init
    Set StackAllocZero = pCallObj
End Property
Private Sub Init()
    With Code
        .Code(0) = &H424448B
        .Code(1) = &H3DD08B
        .Code(2) = &H8D000010
        .Code(3) = &H7208244C
        .Code(4) = &HE98114
        .Code(5) = &H2D000010
        .Code(6) = &H1000
        .Code(7) = &H3D0185
        .Code(8) = &H73000010
        .Code(9) = &H8BC82BEC
        .Code(10) = &H8B0185C4
        .Code(11) = &H50008BE1
        .Code(12) = &HF98B5751
        .Code(13) = &HCA8BC033
        .Code(14) = &H585FAAF3
        .Code(15) = &HCCCCCCC3
        Set pCallObj = InitDelegator(FD, VarPtr(.Code(0)))
    End With
End Sub

'****************************
'Code without instructions shown
'#define _PAGESIZE_ 0x1000
'_declspec(naked) void* _stdcall StackAlloc(long Bytes)
'{
'    _asm
'    {
'        // The code below expects the return address to be the only thing
'        // on the stack and the Bytes parameter to be in eax, so we have
'        // to make it so.
'        mov eax, [esp + 4]
'        mov edx, eax // Save count for inline ZeroMemory loop below
'
'        // Here on down is stolen from Intel\chkstk.asm.
'        // There are a couple of changes:
'        // 1) ecx is not maintained (this is only use to alloc, not probe)
'        // 2) The stack pointer is return in eax
'
'        //push    ecx                     ; save ecx
'        cmp     eax,_PAGESIZE_          ; more than one page requested?
'        lea     ecx,[esp] + 8           ;   compute new stack pointer in ecx
'                                        ;   correct for return address and
'                                        ;   Bytes parameter //saved ecx
'        jb      short lastpage          ; no
'
'probepages:
'        sub     ecx,_PAGESIZE_          ; yes, move down a page
'        sub     eax,_PAGESIZE_          ; adjust request and...
'
'        test    dword ptr [ecx],eax     ; ...probe it
'
'        cmp     eax,_PAGESIZE_          ; more than one page requested?
'        jae     short probepages        ; no
'
'lastpage:
'        sub     ecx,eax                 ; move stack down by eax
'        mov     eax,esp                 ; save current tos and do a...
'
'        test    dword ptr [ecx],eax     ; ...probe in case a page was crossed
'
'        mov     esp,ecx                 ; set the new stack pointer
'
'        //mov     ecx,dword ptr [eax]     ; recover ecx
'        //mov     eax,dword ptr [eax + 4] ; recover return address
'        mov     eax,dword ptr [eax]       ; recover return address
'
'        push    eax                     ; prepare return address
'                                        ; ...probe in case a page was crossed
'
'        // Zero memory work
'        // Note that this isn't optimized very well, it should
'        // do the same as memset, which is to set aligned dwords
'        // with stosd instead of all bytes with stos
'        push ecx              // The return value is currently in ecx, push
'        push edi              // Save edi
'        mov edi, ecx          // Stack pointer is still in ecx
'        xor eax, eax          // eax = 0
'        mov ecx, edx          // Move starting byte count into ecx
'
'        rep stos              // zero the bytes
'
'        pop edi               // restore edi
'        pop eax               // get the return value back
'
'        ret
'
'    }
'}

'****************************
'Code with instructions shown
'#define _PAGESIZE_ 0x1000
'_declspec(naked) void* _stdcall StackAlloc(long Bytes)
'{
'_asm
'{
'// The code below expects the return address to be the only thing
'// on the stack and the Bytes parameter to be in eax, so we have
'// to make it so.
'mov eax, [esp + 4]
'8B 44 24 04
'mov edx, eax // Save count for inline ZeroMemory loop below
'8B D0
'
'// Here on down is stolen from Intel\chkstk.asm.
'// There are a couple of changes:
'// 1) ecx is not maintained (this is only use to alloc, not probe)
'// 2) The stack pointer is returned in eax
'
'//push    ecx                     ; save ecx
'cmp     eax,_PAGESIZE_          ; more than one page requested?
'3D 00 10 00 00
'lea     ecx,[esp] + 8           ;   compute new stack pointer in ecx
'8D 4C 24 08
'                                ;   correct for return address and
'                                ;   Bytes parameter //saved ecx
'jb      short lastpage          ; no
'72 14
'
'probepages:
'sub     ecx,_PAGESIZE_          ; yes, move down a page
'81 E9 00 10 00 00
'sub     eax,_PAGESIZE_          ; adjust request and...
'2D 00 10 00 00
'
'test    dword ptr [ecx],eax     ; ...probe it
'85 01
'
'cmp     eax,_PAGESIZE_          ; more than one page requested?
'3D 00 10 00 00
'jae     short probepages        ; no
'73 EC
'
'lastpage:
'sub     ecx,eax                 ; move stack down by eax
'2B C8
'mov     eax,esp                 ; save current tos and do a...
'8B C4
'
'test    dword ptr [ecx],eax     ; ...probe in case a page was crossed
'85 01
'
'mov     esp,ecx                 ; set the new stack pointer
'8B E1
'
'//mov     ecx,dword ptr [eax]     ; recover ecx
'//mov     eax,dword ptr [eax + 4] ; recover return address
'mov     eax,dword ptr [eax]       ; recover return address
'8B 00
'
'push    eax                     ; prepare return address
'50
'                                ; ...probe in case a page was crossed
'
'// Zero memory work
'// Note that this isn't optimized very well, it should
'// do the same as memset, which is to set aligned dwords
'// with stosd instead of all bytes with stos
'push ecx              // The return value is currently in ecx, push
'51
'push edi              // Save edi
'57
'mov edi, ecx          // The return value is the start of the block to zero
'8B F9
'xor eax, eax          // eax = 0
'33 C0
'mov ecx, edx          // Move starting byte count into ecx
'8B CA
'
'rep stos              // zero the bytes
'F3 AA
'
'pop edi               // restore edi
'5F
'pop eax               // get the return value back
'58
'
'ret
'C3
'
'}
'}

